#include "experiment.h"
#include "algorithm.h"
#include<QDebug>

Experiment::Experiment()
{
    filePath="新建";
    bIsUsedTune=false;
}

bool Experiment::SetFilePath(QString filePath)
{
    this->filePath=filePath;
}
QString Experiment::GetFilePath()
{
    return filePath;
}

AnalyteDataBase *Experiment::GetAnalyteDataBase()
{
   return  &analyteDataBase;
}
bool Experiment::SetAnalyteDataBase(AnalyteDataBase data)
{
    analyteDataBase=data;
    return true;
}
CalRatio Experiment::GetMassCalStandard()
{
    return this->massCalStandard;
}
CalRatio Experiment::GetMassCalHigh()
{
    return this->massCalHigh;
}
CalRatio Experiment::GetCrossCal()
{
    return this->crossCal;
}
TuneResult* Experiment::GetTuneResult()
{
    return &tuneResult;
}

ExpSample *Experiment::GetExpSamplePtr()
{
    return &expSample;
}
ExpSample Experiment::GetExpSample()
{
    return expSample;
}

int Experiment::IsFinished()
{
    foreach(SampleInfor sampleInfor,expSample.GetSampleInfors())
    {
        if(sampleInfor.scanState==0)
        {
           return 0;
        }
        else if(sampleInfor.scanState==1)
        {
           return 1;
        }
    }
    return 2;
}
void Experiment::ClearResult()
{
    //expResultss.clear();
}
bool Experiment::EnableTune(bool state)
{
    this->bIsUsedTune=state;
}
bool Experiment::IsUsedTune()
{
    return this->bIsUsedTune;
}
bool Experiment::SetExpConllectPara()
{

}
ExpConllectPara *Experiment::GetExpConllectParaPtr()
{
    return &this->expConllectPara;
}
bool Experiment::SetExtractionTime()
{

}
ExtractionTime *Experiment::GetExtractionTimePtr()
{
    return &this->extractionTime;
}

ExpMethod *Experiment::GetExpMethodPtr()
{
    return &expMethod;
}
ExpMethod Experiment::GetExpMethod()
{
    return expMethod;
}

IsotopePara *Experiment::GetIsotopeParaPtr()
{
    return &(this->isotopePara);
}

QMap<double,QString> Experiment::GetQuantitativeElement(int type)
{
    QMap<double,QString> elements=analyteDataBase.GetAnalyteElementPairs();
    QMap<double,QString> tempElements;

    switch(type)
    {
    case 0://内标
        foreach(double mass,elements.keys())
        {
            QString name=elements.value(mass);
            InternalPara para=expMethod.GetEleMethod(name).internalPara;//QuantitativePara
            bool type=para.type;
            if(type==0) continue;
            tempElements.insert(mass,name);
        }
        break;
    case 1://非内标
        foreach(double mass,elements.keys())
        {
            QString name=elements.value(mass);
            InternalPara para=expMethod.GetEleMethod(name).internalPara;//QuantitativePara
            bool type=para.type;
            if(type==1) continue;//内标
            tempElements.insert(mass,name);
        }
        break;
    case 2://全定量
        foreach(double mass,elements.keys())
        {
            QString name=elements.value(mass);
            InternalPara para=expMethod.GetEleMethod(name).internalPara;//QuantitativePara
            bool type=para.type;
            if(type==1) continue;
            QuantitativePara para1=expMethod.GetEleMethod(name).quantitativePara;//QuantitativePara
            if(para1.quantitativeMethod=="全定量")
            {
                tempElements.insert(mass,name);
            }
        }
        break;
    case 3://标准加入法
        foreach(double mass,elements.keys())
        {
            QString name=elements.value(mass);
            InternalPara para=expMethod.GetEleMethod(name).internalPara;//QuantitativePara
            bool type=para.type;
            if(type==1) continue;
            QuantitativePara para1=expMethod.GetEleMethod(name).quantitativePara;//QuantitativePara
            if(para1.quantitativeMethod=="标准加入法")
            {
                tempElements.insert(mass,name);
            }
        }
        break;
    }

    return tempElements;
}

bool Experiment::GetScanPara(QList<ScanPara> &scanParas,int type)
{
    if(type==0)//pre
    {
        if(expConllectPara.bPreScan==false) false;
        scanParas=Algorithm::GetSweepScanPara(expConllectPara.GetPreSweepScanParas());
    }
    else//main
    {
        if(expConllectPara.bScan==false)//hop
        {
            scanParas=Algorithm::GetHopScanPara(expConllectPara.GetHopScanParas());
        }
        else
        {
            scanParas=Algorithm::GetSweepScanPara(expConllectPara.GetSweepScanParas());
        }
    }
    return true;
}
int Experiment::GetScanPosition(QList<ScanPara> scanParas,QString name)
{
    double mass=analyteDataBase.GetElementMass(name);
    int index=-1;
    for(int i=0;i<scanParas.count();i++)
    {
        ScanPara scanPara=scanParas.at(i);
        for(int j=0;j<scanPara.channels;j++)
        {
            index++;
            //qDebug()<<mass<<index<<scanPara.startMassNum<<scanPara.interval<<scanPara.channels;
            double value=scanPara.startMassNum+j*scanPara.interval;
            if(mass==value)//mass==scanPara.startMassNum+j*scanPara.interval //出错？
            {
               return index;
            }
        }
    }
    return -1;
}
int Experiment::GetScanPosition(QList<ScanPara> scanParas,double mass)
{
    int index=-1;
    for(int i=0;i<scanParas.count();i++)
    {
        ScanPara scanPara=scanParas.at(i);
        for(int j=0;j<scanPara.channels;j++)
        {
            index++;
            if(mass==scanPara.startMassNum+j*scanPara.interval)
            {
               return index;
            }
        }
    }
    return -1;
}

//result
void Experiment::DoCpsCal()
{
    QList<ScanPara> scanParas;
    if(GetScanPara(scanParas,1)==false) return;
    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor* pSampleInfor=expSample.GetSampleInforPtr(i);
        for(int j=0;j<pSampleInfor->mainScanCount;j++)
        {
            QHash<QString,double> cpsCals;
            MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
            QList<ExpRawData> datas=pMainScanResult->GetMainExpRawData();
            //todo
            for(int k=0;k<scanParas.count();k++)
            {
               QString name=scanParas.at(k).name;
               int index=GetScanPosition(scanParas,name);
               double value=(index<0)?0:datas.at(index).plusedValue;
               cpsCals.insert(name,value);
               //qDebug()<<"name"<<name<<"index"<<index<<"value"<<value;
            }
            pMainScanResult->SetCpsCals(cpsCals);
        }
    }
}
void Experiment::DoCpsJamming()
{
    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor* pSampleInfor=expSample.GetSampleInforPtr(i);
        SampleInfor sampleBlank;
        bool state=expSample.GetBlank(sampleBlank);
        for(int j=0;j<pSampleInfor->mainScanCount;j++)
        {
            QHash<QString,double> cpsJammings;
            MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
            QHash<QString,double> datas=pMainScanResult->GetCpsCals();

            foreach(QString name,datas.keys())//todo
            {
               double blank=0;
               if(state)
               {
                   MainScanResult* pMainScanResult=sampleBlank.GetMainScanResultPtr(j);
                   QHash<QString,double> datas=pMainScanResult->GetCpsCals();
                   blank=datas.value(name);
               }
               cpsJammings.insert(name,datas.value(name)-blank);
            }
            pMainScanResult->SetCpsJammings(cpsJammings);
        }
    }
}
void Experiment::DoIsotope()
{
    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor* pSampleInfor=expSample.GetSampleInforPtr(i);
        for(int j=0;j<pSampleInfor->mainScanCount;j++)
        {
            QHash<QString,double> isotopes;
            MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
            QHash<QString,double> datas=pMainScanResult->GetCpsCals();

            QList<QPair<QString,QString> > isotopeRatios=isotopePara.GetIsotopePara();
            for(int k=0;k<isotopeRatios.count();k++)
            //foreach(QPair<QString,QString> pair,isotopeRatios)//todo
            {
               QPair<QString,QString> pair=isotopeRatios.at(k);
               double value=datas.value(pair.second);
               value=(value==0)?0:datas.value(pair.first)/value;
               isotopes.insert(pair.first+"/"+pair.second,value);
            }
            pMainScanResult->SetIsotopes(isotopes);
        }
    }
}
void Experiment::DoCnCal()
{
    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor* pSampleInfor=expSample.GetSampleInforPtr(i);
        for(int j=0;j<pSampleInfor->mainScanCount;j++)
        {
            QHash<QString,double> cnCals;
            MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
            QHash<QString,double> datas=pMainScanResult->GetCpsInternals();//GetCpsJammings()
            //todo
            foreach(QString name,datas.keys())
            {
               EleMethod *pEleMethod =expMethod.GetEleMethodPtr(name);
               QuantitativePara* pQuantitativePara=pEleMethod->GetQuantitativeParaPtr();
               InternalPara* pInternalPara=pEleMethod->GetInternalParaPtr();
               double cn;
               if(pInternalPara->type==0)//非内标
               {
                  cn=pQuantitativePara->GetCn(datas.value(name));
               }
               else
               {
                   cn=datas.value(name);
               }
               cnCals.insert(name,cn);
            }
            pMainScanResult->SetCnCals(cnCals);
        }
    }
}
//rsd
void Experiment::DoRsdCnCal()
{
    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor* pSampleInfor=expSample.GetSampleInforPtr(i);      

        QStringList elements;
        for(int j=0;j<pSampleInfor->mainScanCount;j++)
        {
            MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
            QHash<QString,double> datas=pMainScanResult->GetCnCals();
            elements=datas.keys();
        }
        QHash<QString,QList<double> > datass;
        foreach(QString name,elements)
        {
            QList<double> temps;
            for(int j=0;j<pSampleInfor->mainScanCount;j++)
            {
                MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
                QHash<QString,double> datas=pMainScanResult->GetCnCals();
                temps.append(datas.value(name));
            }
            datass.insert(name,temps);
        }

        QHash<QString,double> rsds;
        foreach(QString name,datass.keys())
        {
            double rsd=Algorithm::RsdData(datass.value(name));
            rsds.insert(name,rsd);
        }

        pSampleInfor->SetRsdCnCals(rsds);
    }
}
void Experiment::DoRsdCpsCal()
{   
    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor* pSampleInfor=expSample.GetSampleInforPtr(i);

        QStringList elements;
        for(int j=0;j<pSampleInfor->mainScanCount;j++)
        {
            MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
            QHash<QString,double> datas=pMainScanResult->GetCpsCals();
            elements=datas.keys();
        }
        QHash<QString,QList<double> > datass;
        foreach(QString name,elements)
        {
            QList<double> temps;
            for(int j=0;j<pSampleInfor->mainScanCount;j++)
            {
                MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
                QHash<QString,double> datas=pMainScanResult->GetCpsCals();
                temps.append(datas.value(name));
            }
            datass.insert(name,temps);
        }

        QHash<QString,double> rsds;
        foreach(QString name,datass.keys())
        {
            double rsd=Algorithm::RsdData(datass.value(name));
            rsds.insert(name,rsd);
        }

        pSampleInfor->SetRsdCpsCals(rsds);
    }
}
void Experiment::DoRsdCpsJamming()
{  
    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor* pSampleInfor=expSample.GetSampleInforPtr(i);

        QStringList elements;
        for(int j=0;j<pSampleInfor->mainScanCount;j++)
        {
            MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
            QHash<QString,double> datas=pMainScanResult->GetCpsJammings();
            elements=datas.keys();
        }
        QHash<QString,QList<double> > datass;
        foreach(QString name,elements)
        {
            QList<double> temps;
            for(int j=0;j<pSampleInfor->mainScanCount;j++)
            {
                MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
                QHash<QString,double> datas=pMainScanResult->GetCpsJammings();
                temps.append(datas.value(name));
            }
            datass.insert(name,temps);
        }

        QHash<QString,double> rsds;
        foreach(QString name,datass.keys())
        {
            double rsd=Algorithm::RsdData(datass.value(name));
            rsds.insert(name,rsd);
        }

        pSampleInfor->SetRsdCpsJammings(rsds);
    }
}
void Experiment::DoRsdIsotope()
{   
    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor* pSampleInfor=expSample.GetSampleInforPtr(i);

        QStringList elements;
        for(int j=0;j<pSampleInfor->mainScanCount;j++)
        {
            MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
            QHash<QString,double> datas=pMainScanResult->GetIsotopes();
            elements=datas.keys();
        }
        QHash<QString,QList<double> > datass;
        foreach(QString name,elements)
        {
            QList<double> temps;
            for(int j=0;j<pSampleInfor->mainScanCount;j++)
            {
                MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
                QHash<QString,double> datas=pMainScanResult->GetIsotopes();
                temps.append(datas.value(name));
            }
            datass.insert(name,temps);
        }

        QHash<QString,double> rsds;
        foreach(QString name,datass.keys())
        {
            double rsd=Algorithm::RsdData(datass.value(name));
            rsds.insert(name,rsd);
        }

        pSampleInfor->SetRsdIsotopes(rsds);
    }
}

void Experiment::CalCpsInternals()
{
    QHash<QString,double> firstInternalCpss;
    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor* pSampleInfor=expSample.GetSampleInforPtr(i);
        for(int j=0;j<pSampleInfor->mainScanCount;j++)
        {
            MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
            QHash<QString,double> datas=pMainScanResult->GetCpsJammings();
            QMap<double,QString> elements=GetQuantitativeElement(0);
            foreach(QString name,elements.values())
            {
                //if(datas.count()<=0) break;
                double value=datas.value(name)/100;
                if(value==0)
                {
                    value=1;
                }
                firstInternalCpss.insert(name,value);
            }
            break;
        }
        break;
    }

    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor* pSampleInfor=expSample.GetSampleInforPtr(i);
        for(int j=0;j<pSampleInfor->mainScanCount;j++)
        {
            MainScanResult* pMainScanResult=pSampleInfor->GetMainScanResultPtr(j);
            QHash<QString,double> datas=pMainScanResult->GetCpsJammings();

            //内标拟合
            QList<double> ratios;
            QMap<double,QString> elements=GetQuantitativeElement(0);
            if(elements.count()>=2)
            {
                QList<double> ws;
                QList<double> xs;
                QList<double> ys;
                int dimension=1;//"一次函数"
                for(int i=0;i<dimension+1;i++)
                {
                    ratios.append(0);
                }

                foreach(QString name,elements.values())
                {
                    ys.append(datas.value(name));
                    xs.append(analyteDataBase.GetElementMass(name));
                    ws.append(1);
                }
                Algorithm::PolyFit(ws,xs,ys,ratios);
            }
            pMainScanResult->SetRatios(ratios);

            QHash<QString,double> cpsInternals;
            foreach(QString name,datas.keys())
            {
                //计算内标Cps
                EleMethod *pEleMethod =expMethod.GetEleMethodPtr(name);
                InternalPara* pInternalPara=pEleMethod->GetInternalParaPtr();
                QString nameType=pInternalPara->internalS;

                double internalCps=0;
                if(pInternalPara->type==0)//非内标
                {
                    if(nameType=="无")
                    {
                        internalCps=0;//
                    }
                    else if(nameType=="插值")
                    {
                        //计算拟合
                        double value=analyteDataBase.GetElementMass(name);
                        internalCps=pMainScanResult->GetInternalCps(value);
                    }
                    else //其他内标元素
                    {
                        internalCps=datas.value(nameType);
                    }
                }
                else
                {
                    internalCps=firstInternalCpss.value(name);
                }

               //todo
               double cpsInternal=(internalCps==0)?0:datas.value(name)/internalCps;
               cpsInternals.insert(name,cpsInternal);
            }
            pMainScanResult->SetCpsInternals(cpsInternals);
        }
    }
}

//standard
void Experiment::CalStandardPara()
{
    QList<SampleInfor> sample1s,sample2s;
    for(int i=0;i<expSample.GetSampleCount();i++)
    {
        SampleInfor sampleInfor=expSample.GetSampleInfor(i);
        if(sampleInfor.sampleType=="全定量标准")//
        {
            sample1s.append(sampleInfor);
        }
        else if(sampleInfor.sampleType=="标准加入标准")
        {
            sample2s.append(sampleInfor);
        }
    }

    QMap<double,QString> elements;
    elements=GetQuantitativeElement(2);
    foreach(QString name,elements.values())//element
    {
        QList<QuantitativeParaCal> datas;
        QuantitativeParaCal data;
        for(int i=0;i<sample1s.count();i++)//sample
        {
            SampleInfor sampleInfor=sample1s.at(i);
            QHash<QString,double> xs=sampleInfor.GetCnInputs();
            data.cnRef=xs.value(name);

            QList<double> tempys;
            for(int j=0;j<sampleInfor.mainScanCount;j++)
            {
                MainScanResult* pMainScanResult=sampleInfor.GetMainScanResultPtr(j);
                QHash<QString,double> temps=pMainScanResult->GetCpsInternals();//GetCpsJammings()
                tempys.append(temps.value(name));
            }
            data.cpsMean=Algorithm::MeanData(tempys);
            //other
            data.sampleName=sampleInfor.sampleName;
            data.cn=data.cnRef;
            data.error=0;
            data.rError=0;
            //data.cpss=;
            data.bIsUsed=true;

            datas.append(data);
        }

        EleMethod *pEleMethod =expMethod.GetEleMethodPtr(name);
        QuantitativePara* pQuantitativePara=pEleMethod->GetQuantitativeParaPtr();
        pQuantitativePara->SetCalDatas(datas);

        CalStandardCurve(name);
    }

    elements=GetQuantitativeElement(3);
    foreach(QString name,elements.values())//element
    {
        QList<QuantitativeParaCal> datas;
        QuantitativeParaCal data;

        for(int i=0;i<sample2s.count();i++)//sample
        {
            SampleInfor sampleInfor=sample2s.at(i);
            QHash<QString,double> xs=sampleInfor.GetCnInputStandards();
            data.cnRef=xs.value(name);

            QList<double> tempys;
            for(int j=0;j<sampleInfor.mainScanCount;j++)
            {
                MainScanResult* pMainScanResult=sampleInfor.GetMainScanResultPtr(j);
                QHash<QString,double> temps=pMainScanResult->GetCpsInternals();//GetCpsJammings()
                tempys.append(temps.value(name));
            }
            data.cpsMean=Algorithm::MeanData(tempys);
            //other
            data.sampleName=sampleInfor.sampleName;
            data.cn=data.cnRef;
            data.error=0;
            data.rError=0;
            //data.cpss=;
            data.bIsUsed=true;

            datas.append(data);
        }

        EleMethod *pEleMethod =expMethod.GetEleMethodPtr(name);
        QuantitativePara* pQuantitativePara=pEleMethod->GetQuantitativeParaPtr();
        pQuantitativePara->SetCalDatas(datas);

        CalStandardCurve(name);
    }
}
void Experiment::CalStandardCurve(QString name)
{
    EleMethod *pEleMethod =expMethod.GetEleMethodPtr(name);
    QuantitativePara* pQuantitativePara=pEleMethod->GetQuantitativeParaPtr();
    QList<QuantitativeParaCal> datas=pQuantitativePara->GetCalDatas();

    QList<double> ratios;
    QList<double> xs;
    QList<double> ys;
    QList<double> ws;
    int dimension=1;

    if(pQuantitativePara->functionCount=="一次函数")
    {
       dimension =1;
    }
    else if(pQuantitativePara->functionCount=="二次函数")
    {
        dimension=2;
    }
    for(int i=0;i<dimension+1;i++)
    {
        ratios.append(0);
    }

    foreach(QuantitativeParaCal data,datas)
    {
        ys.append(data.cnRef);
        xs.append(data.cpsMean);

        double weight;
        if(pQuantitativePara->weight=="无")
        {
           weight=1;
        }
        else if(pQuantitativePara->weight=="1/SD")
        {
            weight=1;
        }
        else if(pQuantitativePara->weight=="1/浓度")
        {
            weight=(data.cnRef==0)?1:1/data.cnRef;
        }
        else if(pQuantitativePara->weight=="过原点")
        {
            weight=1;
        }
        else if(pQuantitativePara->weight=="过标准加入零点")
        {
            weight=1;
        }
        ws.append(1);
    } 
    Algorithm::PolyFit(ws,xs,ys,ratios);//xx yy
    pQuantitativePara->SetRatios(ratios);
}

void Experiment::Analyzed()
{
    DoCpsCal();//先计算;
    DoCpsJamming();
    CalCpsInternals();
    DoIsotope();
    DoCnCal();

    DoRsdCpsCal();
    DoRsdCpsJamming();
    DoRsdIsotope();

    CalStandardPara();
    DoRsdCnCal();//先计算 CalStandardPara();
}

QDataStream & operator<< ( QDataStream & stream, const Experiment & experiment )
{
    stream<<experiment.analyteDataBase
            <<experiment.massCalStandard
            <<experiment.massCalHigh
            <<experiment.crossCal
            <<experiment.expConllectPara
            <<experiment.tuneResult
            <<experiment.bIsUsedTune
            <<experiment.extractionTime
            <<experiment.isotopePara
            <<experiment.expMethod
            <<experiment.expSample;
            //<<experiment.expResultss;
    return stream;
}

QDataStream &	operator>> ( QDataStream & stream, Experiment & experiment )
{
    stream>>experiment.analyteDataBase
            >>experiment.massCalStandard
            >>experiment.massCalHigh
            >>experiment.crossCal
            >>experiment.expConllectPara
            >>experiment.tuneResult
            >>experiment.bIsUsedTune
            >>experiment.extractionTime
            >>experiment.isotopePara
            >>experiment.expMethod
            >>experiment.expSample;
            //>>experiment.expResultss;
    return stream;
}
